package com.globallogic.socialnetwork.dao;

import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Restrictions;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import com.globallogic.socialnetwork.entity.Group;
import com.globallogic.socialnetwork.entity.Message;
import com.globallogic.socialnetwork.entity.User;
import com.globallogic.socialnetwork.entity.Friend;
import com.globallogic.socialnetwork.util.DaoException;

/**
 * Implementation of the User dao.
 */
@Repository("userDao")
public class UserDao {

	private static final Logger log = Logger.getLogger("SocialNetworkFileLogger");
	Criteria criteria;
	@Autowired
	private SessionFactory sessionFactory;

	public SessionFactory getSessionFactory() {
		return sessionFactory;
	}

	public void setSessionFactory(SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}

	/**
	 * Create new object in the DB.
	 * 
	 * @param user
	 *            object to store
	 * @throws DaoException
	 */
	@Transactional
	public void createUser(User user) throws DaoException {
		try {
			getSessionFactory().getCurrentSession().persist(user);
		} catch (HibernateException e) {
			log.warn(e);
			throw new DaoException(e.toString());
		}
	}

	/**
	 * Return user by login
	 * 
	 * @param login
	 * @return found <tt>User</tt>, <tt>null</tt> if not found.
	 * @throws DaoException
	 */
	@Transactional
	public User getUserByLogin(String login) throws DaoException {
		User user = null;
		try {
			criteria = getSessionFactory().getCurrentSession().createCriteria(
					User.class);
			criteria.add(Restrictions.like("login", login));
			user = (User) criteria.uniqueResult();
		} catch (HibernateException e) {
			log.warn(e);
			throw new DaoException(e.toString());
		}
		return user;
	}

	/**
	 * Return user by login and password
	 * 
	 * @param login
	 * @param password
	 * @return <tt>User</tt>, <tt>null</tt> if not found.
	 * @throws DaoException
	 */
	@Transactional
	public User getUserByLoginPassword(String login, String password)
			throws DaoException {
		User user = null;
		try {
			criteria = getSessionFactory().getCurrentSession().createCriteria(
					User.class);
			criteria.add(Restrictions.like("login", login)).add(
					Restrictions.like("password", password));
			user = (User) criteria.uniqueResult();
		} catch (HibernateException e) {
			log.warn(e);
			throw new DaoException(e.toString());
		}
		return user;
	}

	/**
	 * Update old object in the DB.
	 * 
	 * @param user
	 *            object to update
	 * @throws DaoException
	 */
	@Transactional
	public void updateUser(User user) throws DaoException {
		try {
			getSessionFactory().getCurrentSession().update(user);
		} catch (HibernateException e) {
			log.warn(e);
			throw new DaoException(e.toString());
		}

	}

	/**
	 * Get all users from database
	 * 
	 * @return list of <tt>User</tt>, <tt>null</tt> if not found.
	 * @throws DaoException
	 */
	@SuppressWarnings("unchecked")
	@Transactional
	public List<User> getAllUsers() throws DaoException {
		List<User> users = null;
		try {
			users = getSessionFactory().getCurrentSession()
					.createCriteria(User.class).list();
		} catch (HibernateException e) {
			log.warn(e);
			throw new DaoException(e.toString());
		}

		return users;
	}

	/**
	 * Get list of users by filter
	 * 
	 * @param filter
	 *            attribute's name in class {@link User} for search users
	 * @param condition
	 *            condition for search users
	 * @return list of <tt>User</tt>, <tt>null</tt> if not found.
	 * @throws DaoException
	 */
	@SuppressWarnings("unchecked")
	@Transactional
	public List<User> getUsersByFilter(String filter, String condition)
			throws DaoException {
		List<User> users = null;
		try {
			if (StringUtils.isBlank(condition)) {
				criteria = getSessionFactory().getCurrentSession()
						.createCriteria(User.class);
			} else {
				criteria = getSessionFactory().getCurrentSession()
						.createCriteria(User.class)
						.add(Restrictions.like(filter, condition));
			}
			users = criteria.list();
		} catch (HibernateException e) {
			log.warn(e);
			throw new DaoException(e.toString());
		}

		return users;
	}

	/**
	 * Follow one user to another
	 * 
	 * @param proposer
	 * @param accepter
	 * @throws DaoException
	 */
	@Transactional
	public void followUser(User proposer, User accepter) throws DaoException {
		try {
			Friend friend = new Friend(true, accepter, proposer);
			getSessionFactory().getCurrentSession().persist(friend);
		} catch (HibernateException e) {
			log.warn(e);
			throw new DaoException(e.toString());
		}
	}

	/**
	 * Get all user's friend
	 * 
	 * @param user
	 * @return list of friends, <tt>null</tt> if not found.
	 * @throws DaoException
	 */
	@SuppressWarnings("unchecked")
	@Transactional
	public List<User> getFriends(User user) throws DaoException {
		List<User> friends = null;
		try {
			friends = new ArrayList<User>();
			criteria = getSessionFactory()
					.getCurrentSession()
					.createCriteria(Friend.class)
					.add(Restrictions.or(Restrictions.like("proposer", user),
							Restrictions.like("accepter", user)));

			List<Friend> friendsList = criteria.list();

			for (Friend friend : friendsList) {
				if (!friend.getIsNew())
					if (friend.getAccepter().equals(user))
						friends.add(friend.getProposer());
					else
						friends.add(friend.getAccepter());
			}
		} catch (HibernateException e) {
			log.warn(e);
			throw new DaoException(e.toString());
		}
		return friends;
	}

	/**
	 * Get all user's invitations
	 * 
	 * @param user
	 * @return list of new friends, <tt>null</tt> if not found.
	 * @throws DaoException
	 */
	@SuppressWarnings("unchecked")
	@Transactional
	public List<User> getNewFriends(User user) throws DaoException {
		List<User> friends = null;
		try {
			Query query = getSessionFactory()
					.getCurrentSession()
					.createQuery(
							"SELECT k.proposer FROM Friend k"
									+ " WHERE k.accepter=:User and k.isNew=true")
					.setParameter("User", user);

			friends = (List<User>) query.list();
		} catch (HibernateException e) {
			log.warn(e);
			throw new DaoException(e.toString());
		}
		return friends;
	}

	/**
	 * Accept invitation
	 * 
	 * @param user
	 *            User, that accept invitation
	 * @param proposer
	 *            User, that send invitation
	 * @throws DaoException
	 */
	@Transactional
	public void acceptFriend(User user, User proposer) throws DaoException {
		try {
			criteria = getSessionFactory().getCurrentSession()
					.createCriteria(Friend.class)
					.add(Restrictions.like("accepter", user))
					.add(Restrictions.like("proposer", proposer));
			Friend target = (Friend) criteria.uniqueResult();
			target.setIsNew(false);
			getSessionFactory().getCurrentSession().update(target);

		} catch (HibernateException e) {
			log.warn(e);
			throw new DaoException(e.toString());
		}
	}

	/**
	 * Send message
	 * 
	 * @param senderUser
	 *            User, that send message
	 * @param receiverUser
	 *            User, that receive message
	 * @param message
	 *            text of message
	 * @throws DaoException
	 */
	@Transactional
	public void sendMessage(User senderUser, User receiverUser, String message)
			throws DaoException {
		try {
			Message msg = new Message(false, message, senderUser, receiverUser,
					senderUser);
			getSessionFactory().getCurrentSession().persist(msg);
			msg = new Message(true, message, senderUser, receiverUser,
					receiverUser);
			getSessionFactory().getCurrentSession().persist(msg);
		} catch (HibernateException e) {
			log.warn(e);
			throw new DaoException(e.toString());
		}
	}

	/**
	 * Get all user's messages
	 * 
	 * @param user
	 * @return list of <tt>messages</tt>, <tt>null</tt> if not found.
	 * @throws DaoException
	 */
	@SuppressWarnings("unchecked")
	@Transactional
	public List<Message> getAllMessages(User user) throws DaoException {
		List<Message> messages = null;
		try {
			criteria = getSessionFactory().getCurrentSession()
					.createCriteria(Message.class)
					.add(Restrictions.like("keeper", user));
			messages = criteria.list();
		} catch (HibernateException e) {
			log.warn(e);
			throw new DaoException(e.toString());
		}
		return messages;
	}

	/**
	 * Update message isNew=false
	 * 
	 * @param selectedMessage
	 * @throws DaoException
	 */
	@Transactional
	public void markAsRead(Message selectedMessage) throws DaoException {
		try {
			selectedMessage.setisNew(false);
			getSessionFactory().getCurrentSession().update(selectedMessage);
		} catch (HibernateException e) {
			log.warn(e);
			throw new DaoException(e.toString());
		}
	}

	/**
	 * Remove message from db
	 * 
	 * @param selectedMessage
	 * @throws DaoException
	 */
	@Transactional
	public void removeMessage(Message selectedMessage) throws DaoException {
		try {
			getSessionFactory().getCurrentSession().delete(selectedMessage);
		} catch (HibernateException e) {
			log.warn(e);
			throw new DaoException(e.toString());
		}
	}

	/**
	 * remove friend from db
	 * 
	 * @param selectedFriend
	 * @param currentUser
	 * @throws DaoException
	 */
	@Transactional
	public void removeFriend(User selectedFriend, User currentUser)
			throws DaoException {
		try {
			String hqlDelete = "delete from Friend f where f.accepter=:Selected and f.proposer=:Current";
			getSessionFactory().getCurrentSession().createQuery(hqlDelete)
					.setParameter("Selected", selectedFriend)
					.setParameter("Current", currentUser).executeUpdate();
			hqlDelete = "delete from Friend f where f.accepter=:Current and f.proposer=:Selected";
			getSessionFactory().getCurrentSession().createQuery(hqlDelete)
					.setParameter("Selected", selectedFriend)
					.setParameter("Current", currentUser).executeUpdate();
		} catch (HibernateException e) {
			log.warn(e);
			throw new DaoException(e.toString());
		}
	}

	/**
	 * Get groups from db
	 * 
	 * @param condition
	 *            condition for search groups
	 * @return found list of groups, if condition="" - list of all groups
	 * @throws DaoException
	 */
	@SuppressWarnings("unchecked")
	@Transactional
	public List<Group> searchGroups(String condition) throws DaoException {
		List<Group> groups = null;
		try {
			if (StringUtils.isBlank(condition))
				criteria = getSessionFactory().getCurrentSession()
						.createCriteria(Group.class);
			else
				criteria = getSessionFactory().getCurrentSession()
						.createCriteria(Group.class)
						.add(Restrictions.like("nameGroup", condition));

			groups = criteria.list();
		} catch (HibernateException e) {
			log.warn(e);
			throw new DaoException(e.toString());
		}
		return groups;

	}
}